Someone asked me today why they couldn’t run the following code inside of a Task Runner without getting an error that the CFC couldn’t be found:
new lib.providers.s3();
They wanted to know how to create the CFC. I figured the answer(s) were worth sharing since there’s several ways to skin this cat.
Why it doesn’t work
This is because Lucee’s “web root” is not the current working directory that your task is running out of. While Task Runners themselves are “aware” of their surroundings, the native CFML code isn’t. If you were to dump out expandPath( '/' )
from your task, you’d see the folder where Lucee is looking for /lib/providers.s3.cfc
in and it’s probably not at all related to your shell’s current working directory at all. You shouldn’t ever depend on what Lucee thinks the ‘web root’ is since it’s not reliable.
Here’s 4 quick ways to create arbitrary CFCs from inside your Task Runner.
Load the CFCs inside a Module
This is the ideal method as it’s very easy and self-contained. Define a module, whether it’s stored on ForgeBox and installed as a dependency or just something you’ve committed in your code that lives next to the task.cfc
doesn’t matter.
- task.cfc
- modules_app/
- s3Libs/
- ModuleConfig.cfc <- Only needs an empty configure() method
- models/
- providers/
- S3.cfc
Then, you’d just load the module in your task like this
loadModule( 'modules_app/s3Libs' );
and then just use it as
getInstance( 'S3@s3Libs' );
Remember, a loaded module remains in memory even after the task has finished executing until the CLI is closed.
Map a folder of CFCs on-the-fly
WireBox can be asked to scan any folder of CFCs for you and map them. Mapping a dir of CFCs manually would look like this:
wirebox
.getBinder()
.mapDirectory(
packagePath=resolvePath( 'lib/providers' ),
namespace="taskProvidersWhatever"
);
The resolvepath()
will give you the full absolute path to the folder, and the namespace
can be whatever you like. You don’t have to use a namespace, but you want to in order to prevent any of your CFCs overriding core WireBox mappings which could break CommandBox.
then just ask for it
getinstance( 's3@taskProvidersWhatever' )
This is really more/less what happens when a module is loaded except the namespace is just the module name. Remember, mapped CFC’s in WireBox remain in memory even after the task has finished executing until the CLI is closed.
Add a quick and dirty CF Mapping
If you don’t want to use WireBox at all, you can create a CF mapping that points to a folder of your choice, and then use that mapping as the “root” of your CFC path
fileSystemUtil.createMapping( '/taskRoot', resolvepath( '' ) );
That would create a CF mapping in the CLI called /taskRoot
that pointed to the folder the task.cfc
lived in.
Then you can do
new taskRoot.lib.providers.s3()
Remember, CF Mappings created in the CLI remain in memory even after the task has finished executing until the CLI is closed.
Force the CFC path to be relative
This approach still adds a CF mapping behind the scenes. Since a CFC path MUST be relative, CommandBox has a helper that will take an absolute path and force it to be relative, creating new CF mappings where neccessary.
absoluteCFCPath = resolvePath( 'lib/providers/s3' );
relativeCFCPath = fileSystemUtil.makePathRelative( absoluteCFCPath );
// This blind replacement won't work for a Windows UNC network drive, BTW
CFCDotPath = relativeCFCPath.listChangeDelims( '.', '/\' );
createOBject( "component", CFCDotPath ).init()
basically that turns
lib/providers/s3
into
C:/full/path/to/lib/providers/s3
and then turns that into a relative path
/C_DRIVE/full/path/to/lib/providers/s3
(which creates a CF mapping on the fly, if needed)
and then turns that to
C_DRIVE.full.path.to.lib.providers.s3
which becomes your CFC path